﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using ArgusLib.InteropServices;

namespace ArgusLib.FFmpeg.avcodec.Interop
{
	static class Functions
	{
		static Functions()
		{
			//Log.Initialize();
			Functions.avcodec_register_all();
		}

		/// <summary>
		/// Register all the codecs, parsers and bitstream filters which were enabled at
		/// configuration time. If you do not call this function you can select exactly
		/// which formats you want to support, by using the individual registration
		/// functions.
		/// </summary>
		/// <seealso cref="avcodec_register"/>
		/// <seealso cref="av_register_codec_parser"/>
		/// <seealso cref="av_register_bitstream_filter"/>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		static extern void avcodec_register_all();

		/// <summary>
		/// Find a registered encoder with a matching codec ID.
		/// </summary>
		/// <param name="id">AVCodecID of the requested encoder</param>
		/// <returns>An encoder if one was found, NULL otherwise.</returns>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodec>))]
		public static extern AVCodec avcodec_find_encoder(AVCodecID id);

		/// <summary>
		/// Allocate an AVCodecContext and set its fields to default values. The
		/// resulting struct can be deallocated by calling avcodec_close() on it followed
		/// by av_free().
		/// </summary>
		/// <param name="codec">
		/// if non-NULL, allocate private data and initialize defaults
		/// for the given codec. It is illegal to then call avcodec_open2()
		/// with a different codec.
		/// If NULL, then the codec-specific defaults won't be initialized,
		/// which may result in suboptimal default settings (this is
		/// important mainly for encoders, e.g. libx264).
		/// </param>
		/// <returns>An AVCodecContext filled with default values or NULL on failure.</returns>
		/// <seealso cref="avcodec_get_context_defaults"/>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodecContext>))]
		public static extern AVCodecContext avcodec_alloc_context3(
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodec>))]
			AVCodec codec);

		/// <summary>
		/// Initialize the AVCodecContext to use the given AVCodec. Prior to using this
		/// function the context has to be allocated with avcodec_alloc_context3().
		/// 
		/// The functions avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(),
		/// avcodec_find_decoder() and avcodec_find_encoder() provide an easy way for
		/// retrieving a codec.
		/// </summary>
		/// <param name="avctx">The context to initialize.</param>
		/// <param name="codec">
		/// The codec to open this context for. If a non-NULL codec has been
		/// previously passed to avcodec_alloc_context3() or
		/// avcodec_get_context_defaults3() for this context, then this
		/// parameter MUST be either NULL or equal to the previously passed
		/// codec.
		/// </param>
		/// <param name="options">
		/// A dictionary filled with AVCodecContext and codec-private options.
		/// On return this object will be filled with options that were not found.
		/// </param>
		/// <returns>zero on success, a negative value on error</returns>
		/// <example>
		/// <code>
		/// avcodec_register_all();
		/// av_dict_set(&opts, "b", "2.5M", 0);
		/// codec = avcodec_find_decoder(AV_CODEC_ID_H264);
		/// if (!codec)
		///		exit(1);
		///		
		/// context = avcodec_alloc_context3(codec);
		/// 
		/// if (avcodec_open2(context, codec, opts) < 0)
		///		exit(1);
		/// </code>
		/// </example>
		/// <remarks>This function is not thread safe!</remarks>
		/// <seealso cref="avcodec_alloc_context3"/>
		/// <seealso cref="avcodec_find_decoder"/>
		/// <seealso cref="avcodec_find_encoder"/>
		/// <seealso cref="av_dict_set"/>
		/// <seealso cref="av_opt_find"/>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		public static extern int avcodec_open2(
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodecContext>))]
			AVCodecContext avctx,
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodec>))]
			AVCodec codec,
			IntPtr options);

		/// <summary>
		/// Close a given AVCodecContext and free all the data associated with it
		/// (but not the AVCodecContext itself).
		///
		/// Calling this function on an AVCodecContext that hasn't been opened will free
		/// the codec-specific data allocated in avcodec_alloc_context3() /
		/// avcodec_get_context_defaults3() with a non-NULL codec. Subsequent calls will
		/// do nothing.
		/// </summary>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		public static extern int avcodec_close(
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodecContext>), MarshalCookie = ArgusMarshalerCookies.SkipNativeCleanUp)]
			AVCodecContext avctx);

		/// <summary>
		/// Encode a frame of video.
		/// Takes input raw video data from frame and writes the next output packet, if
		/// available, to avpkt. The output packet does not necessarily contain data for
		/// the most recent frame, as encoders can delay and reorder input frames
		/// internally as needed.
		/// </summary>
		/// <param name="avpkt">
		/// Output <see cref="AVPacket"/>
		/// The user can supply an output buffer by setting 
		/// avpkt->data and avpkt->size prior to calling the
		/// function, but if the size of the user-provided data is not
		/// large enough, encoding will fail. All other AVPacket fields
		/// will be reset by the encoder using <see cref="av_init_packet"/>. If
		/// avpkt->data is NULL, the encoder will allocate it.
		/// The encoder will set avpkt->size to the size of the
		/// output packet. The returned data (if any) belongs to the
		/// caller, he is responsible for freeing it.
		/// If this function fails or produces no output, avpkt will be
		/// freed using <see cref="av_free_packet"/> (i.e. avpkt->destruct will be
		/// called to free the user supplied buffer).
		/// </param>
		/// <param name="frame">
		/// <see cref="avutil.AVFrame"/> containing the raw video data to be encoded.
		/// May be NULL when flushing an encoder that has the
		/// CODEC_CAP_DELAY capability set.
		/// </param>
		/// <param name="got_packet_ptr">
		/// This field is set to 1 by libavcodec if the
		/// output packet is non-empty, and to 0 if it is
		/// empty. If the function returns an error, the
		/// packet can be assumed to be invalid, and the
		/// value of got_packet_ptr is undefined and should
		/// not be used.
		/// </param>
		/// <returns>0 on success, negative error code on failure</returns>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		public static extern int avcodec_encode_video2(
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVCodecContext>))]
			AVCodecContext avctx,
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVPacket>))]
			AVPacket avpkt,
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<avutil.AVFrame>))]
            avutil.AVFrame frame,
			out bool got_packet_ptr);

		/// <summary>
		/// Free a packet.
		/// </summary>
		/// <param name="pkt">Packet to free.</param>
		[DllImport(FFmpegDll.avcodec, CallingConvention=FFmpegDll.CallingConvention)]
		public static extern void av_free_packet(
			[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(ArgusMarshaler<AVPacket>), MarshalCookie = ArgusMarshalerCookies.SkipNativeCleanUp)]
			AVPacket pkt);
	}
}
